﻿using System;
using System.Windows;
using System.Collections.Generic;
using System.Windows.Controls;
using System.Windows.Media;
using Bling.DSL;
using Bling.WPF;
using Bling.Shapes;
using Bling.Animation;
using Bling.Util;
using Bling.Properties;
using Bling.Charts;
/// hello world.
namespace Bling.Tutorial {
  public class Pie3DBl : CanvasBl<Pie3D, Pie3DBl> {
    public Pie3DBl(Expr<Pie3D> v) : base(v) { }
    /// <summary>
    /// Create a new pie with specified slices.
    /// </summary>
    /// <param name="Parent">Canvas that contains this pie.</param>
    /// <param name="Slices">Slices of the pie, will be used to sequentially create pie pieces.</param>
    public Pie3DBl(CanvasBl Parent, params DoubleBl[] Slices) : base(Parent, new Pie3D(Slices)) { }
    public static implicit operator Pie3DBl(Expr<Pie3D> v) { return new Pie3DBl(v); }
    public static implicit operator Pie3DBl(Pie3D v) { return new Constant<Pie3D>(v); }
    static Pie3DBl() { Register(v => v); }
    public PieBl Pie {
      get { return CurrentValue.Pie; } 
    }
    public DoubleBl SideHeight { get { return Underlying.Property<double>(Pie3D.SideHeightProperty); } set { SideHeight.Bind = value; } }
    public DoubleBl Squash { get { return Underlying.Property<double>(Pie3D.SquashProperty); } set { Squash.Bind = value; } }
    public DoubleBl Radius {
      get { return Underlying.Property<double>(Pie3D.RadiusProperty); }
      set { Radius.Bind = value; } 
    }
  }
  public class Pie3D : Canvas {
    public readonly Pie Pie;
    public static readonly DependencyProperty RadiusProperty = "Radius".NewProperty<Pie3D, double>(100d).Property;
    public static readonly DependencyProperty SideHeightProperty = "SideHeight".NewProperty<Pie3D, double>(50d).Property;
    public static readonly DependencyProperty SquashProperty = "Squash".NewProperty<Pie3D, double>(.7d).Property;

    public double Radius {
      get { return (double)this.GetValue(RadiusProperty); }
      set { this.SetValue(RadiusProperty, value); }
    }
    public double SideHeight {
      get { return (double)this.GetValue(SideHeightProperty); }
      set { this.SetValue(SideHeightProperty, value); }
    }
    public double Squash {
      get { return (double)this.GetValue(SquashProperty); }
      set { this.SetValue(SquashProperty, value); }
    }
    public Pie3D(params DoubleBl[] Slices) {
      Pie3DBl Self = (this);
      DoubleBl ScaledSideHeight = Self.SideHeight * (1d - Self.Squash);

      var PieCanvas = new CanvasBl(Self) {
        Size = new PointBl(2d, 2d) * Self.Radius + new PointBl(0, ScaledSideHeight),
        Effect = new DropShadowEffectBl() {
          ShadowDepth = 8,
          Direction = 270d.ToDegrees(),
          Opacity = .3,
        },
      };
      this.Pie = new PieBl(PieCanvas, Slices) {
        Size = new PointBl(2d, 2d) * Self.Radius, LeftTop = new PointBl(0, 0),
        ZIndex = 2, // 3D effect will be below this.
      };

      var side = new CanvasBl(PieCanvas) {
        ZIndex = 1, Size = PieCanvas.Size, LeftTop = new PointBl(0, 0),
        Clip = GeometryBl.Ellipse(Self.Pie.CenterPosition + new PointBl(0, ScaledSideHeight), new PointBl(Self.Radius, Self.Radius)) 
      };
      var side2 = new CanvasBl(PieCanvas) {
        ZIndex = 1, Size = PieCanvas.Size, LeftTop = new PointBl(0, 0),
        Clip = GeometryBl.Rectangle(new PointBl(0, Self.Pie.Height * 0.5), new PointBl(Self.Radius * 2, ScaledSideHeight))
      };
      var fillColor = new LinearGradientBrushBl() { StartPoint = new PointBl(0, 1), EndPoint = new PointBl(1, 1) };
      fillColor.Stop(Colors.Black, 0.01).Stop(Colors.Gray, 0.5).Stop(Colors.Black, 0.99);
      var sideX = new RectangleBl(side) {
        Size = side.Size, LeftTop = side.LeftTop, Opacity = 0.45,
        Fill = fillColor, ZIndex = 1,
      };
      var sideX2 = new RectangleBl(side2) {
        Size = side2.Size, LeftTop = side2.LeftTop, Opacity = 0.45,
        Fill = fillColor, ZIndex = 1,
      };

      //var ellipse = new EllipseBl(PieCanvas) {
      //  ZIndex = 3, Size = Self.Pie.Size,
      //  CenterPosition = Self.Pie.CenterPosition + new PointBl(0, ScaledSideHeight),
      //  Stroke = { Brush = Brushes.Transparent, Thickness = 1 },
      //};
      PieCanvas.RenderTransform[new PointBl(0, 0)].Scale = new PointBl(1, Self.Squash);
      var ActualPieCanvasSize = PieCanvas.Size * new PointBl(1, Self.Squash);
      Self.Size = ActualPieCanvasSize + new PointBl(100, 30 + 100 * Squash);
      //PieCanvas.CenterPosition = Self.CenterSize;
      PieCanvas.LeftTop = Self.CenterSize - ActualPieCanvasSize / 2d;

      for (int i = 0; i < Slices.Length; i++) {
        /*
        var colorIndex = r.Next(0, MyColors.Length);
        while (usedColors.Contains(colorIndex))
          colorIndex = r.Next(0, MyColors.Length);
        usedColors.Add(colorIndex);
        pie[i].Fill = MyColors[colorIndex];
        */

        var nextLeftTop = Self.Pie.CenterSize + (Pie.Rotation((i + 1) % Slices.Length).ToPoint() * (Self.Radius));
        var atLeftTop = Self.Pie.CenterSize + (Pie.Rotation(i).ToPoint() * (Self.Radius));
        var labelAngle = (Self.Pie.Rotation(i) + Self.Pie[i].WedgeAngle / 2d).Normalize;
        var ratio = (labelAngle - 1.ToPI()).PI.Abs;

        var labelPos =
          (new PointBl(1d, 1d) * Self.Radius * new PointBl(1, Self.Squash) + PieCanvas.LeftTop) 
          + (labelAngle.ToPoint() * (Self.Radius + 20d + ratio * 20d)) * new PointBl(1, Self.Squash)
          ;
        var labelContent = (Slices[i] / Pie.Total * 100d).Round;
        var label = new LabelBl(Self) {
          ZIndex = 10,
          CenterPosition = labelPos, Content = labelContent.ToStringBl() + "%",
          Font = { Size = 15 + 5 * ratio, Weight = FontWeights.SemiBold, Brush = { Bind = Self.Pie[i].Fill }  },
          Visibility = labelContent > 0,
        };
        Self.Pie[i].ToolTip = labelAngle.ToStringBl(100) + " " + (ratio * 100).Round;// +" " + (ratio3 * 100).Round;
        Self.Pie[i].Stroke.Thickness = 0;

        var isSpecial = Pie.Rotation(i) < 1.ToPI() & (Self.Pie.Rotation(i) + Self.Pie[i].WedgeAngle) > 1.ToPI();
        var specialLeftTop = (Self.Pie.CenterSize + 1.ToPI().ToPoint() * Self.Radius);
        var isBottom = (atLeftTop.X > nextLeftTop.X) | isSpecial;
        var rect = new RectangleBl(side) {
          Fill = Self.Pie[i].Fill,
          Size = new PointBl(20, 20),
          Stroke = { Thickness = 0 },
          Left = isSpecial.Condition(specialLeftTop.X, nextLeftTop.X),
          ZIndex = 0,
        };
        rect.Width = isBottom.Condition(atLeftTop.X - rect.LeftTop.X, 0);
        rect.Bottom = isBottom.Condition(PieCanvas.Height, 0);
        rect.Height = isBottom.Condition(Self.Radius + ScaledSideHeight, 0);
        
        var rect2 = new RectangleBl(side2) {
          Fill = Self.Pie[i].Fill,
          Size = new PointBl(20, 20),
          Stroke = { Thickness = 0 },
          Left = isSpecial.Condition(specialLeftTop.X, nextLeftTop.X),
          ZIndex = 0,
          Height = isBottom.Condition(Self.Radius + ScaledSideHeight, 0),
          Width = rect.Width
        };
      }
    }


  }


  public class PieTest : Canvas {
    /// <summary>
    /// Colors to use in pie chart
    /// </summary>
    private static readonly Color[] MyColors = new Color[] {
      "#c24a2e".Convert<Color>(),
      "#e96749".Convert<Color>(),
      //"#ec9874".Convert<Color>(),
      "#a33255".Convert<Color>(),
      "#cf5872".Convert<Color>(),
      //"#df8398".Convert<Color>(),
      "#385f57".Convert<Color>(),
      "#558577".Convert<Color>(),
      //"#8db29e".Convert<Color>(),
      "#1f72ae".Convert<Color>(),
      "#3188c8".Convert<Color>(),
      //"#63a5d6".Convert<Color>(),
      "#5c397e".Convert<Color>(),
      "#9d61a8".Convert<Color>(),
      //"#ca8acd".Convert<Color>(),
      "#b2712a".Convert<Color>(),
      "#c69746".Convert<Color>(),
      //"#e1be70".Convert<Color>(),
      "#616637".Convert<Color>(),
      "#8c8f4c".Convert<Color>(),
      //"#b6bb7a".Convert<Color>(),
      "#0d7771".Convert<Color>(),
      "#199d8d".Convert<Color>(),
      //"#65bebb".Convert<Color>(),
    };

    public PieTest() { Test1(); }

    public void Test1() {
      CanvasBl MainCanvas = this;
      Random r = new Random();
      var squash = new LabelSliderBl(MainCanvas) {
        Width = MainCanvas.Width, ZIndex = 20, LeftTop = new PointBl(0, 0),
        Minimum = .1, Maximum = 1.0, Value = .7, LabelName = "Squash",
      };
      var sideHeight = new LabelSliderBl(MainCanvas) {
        Width = MainCanvas.Width, ZIndex = 20, LeftTop = squash.LeftBottom,
        Minimum = 0, Maximum = 300, Value = 50, LabelName = "Depth",
      };
      var randomize = new ButtonBl(MainCanvas) {
        Content = "Change", LeftTop = sideHeight.LeftBottom,
      };

      var slices = new DoubleBl[10];
      {
        PointBl P = new PointBl(0, MainCanvas.Height);
        for (int i = 0; i < slices.Length; i++) {
          var slider = new LabelSliderBl(MainCanvas) {
            Minimum = 0, Maximum = 1, Value = { Now = r },
            LeftBottom = P, Width = MainCanvas.Width, WidthRatio = .1, LabelName = "P" + i,
          };
          slices[i] = slider;
          P = slider.LeftTop;
        }
      }
      randomize.Click = () => {
        for (int i = 0; i < slices.Length; i++) {

          slices[i].Animate.Duration(500).Tween(t => t.Ease.Cubic.Out).To = r;
        }
      };
      DoubleBl Radius = 100d;
      var Pie3D = new Pie3DBl(MainCanvas, slices) {
        CenterTop = new PointBl(MainCanvas.CenterSize.X, 100d),
        Radius = Radius, SideHeight = sideHeight, Squash = squash,
        ClipToBounds = true, Background = Brushes.LightGray
      };
      HashSet<int> usedColors = new HashSet<int>();
      for (int i = 0; i < slices.Length; i++) {
        var colorIndex = r.Next(0, MyColors.Length);
        while (usedColors.Contains(colorIndex))
          colorIndex = r.Next(0, MyColors.Length);
        usedColors.Add(colorIndex);
        Pie3D.Pie[i].Fill = MyColors[colorIndex];
      }
    }
  }
}